package cn.yeziji.netty.adapter;

import cn.yeziji.forum.basic.netty.Message;
import cn.yeziji.forum.basic.netty.chat.ChatMessage;
import cn.yeziji.forum.basic.netty.user.UserMessage;
import cn.yeziji.forum.common.key.ChatroomCacheKey;
import cn.yeziji.forum.common.key.MessageKey;
import cn.yeziji.forum.common.key.NoticeCacheKey;
import cn.yeziji.forum.common.key.RabbitMQConfig;
import cn.yeziji.forum.utils.ListsCollect;
import cn.yeziji.forum.utils.RedisUtils;
import cn.yeziji.netty.config.handler.NettyWebsocketHandler;
import com.alibaba.fastjson.JSON;
import io.netty.channel.ChannelHandlerContext;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 私有聊天室适配器
 *
 * <ol>
 *   <li>用户打开聊天室 -- 进入聊天室的时候有 2 种情况。 1 -- 直接进入，没有选择对应的用户；2 -- 选择对话用户进入
 *   <li>创建临时私有聊天室，并发送通知给 {@code forum-dao 服务中的 PrivateChatListener} 处理聊天记录后重新发送通知返回给 {@link
 *       cn.yeziji.netty.config.handler.NettyWebsocketHandler#noticePrivateChat(Message)} 进行实时返回给客户端
 *   <li>发送消息，如果对方用户在线，那么直接发送聊天信息给用户。如果不在线，那么将聊天记录保存至 Redis 的 {@link
 *       ChatroomCacheKey#LEAVE_PRIVATE_CHAT_KEY} 缓存当中，用于用户登录时直接获取通知 <b>该缓存当触发消除消息的事件时才会消除</b>
 * </ul>
 *
 * <p>通知 {@link #notice(ChannelHandlerContext)} 需要返回用户的粉丝用户、关注用户以及聊天过的用户。{@link #data} 中包含了 {@code
 * to} 对象，那么还需要返回 {@code from} 与 {@code to} 的聊天记录
 *
 * @author gzkemays
 * @since 2022/11/08 3:42 PM
 */
public class PrivateChatAdapter extends SelectedTypes<ChatMessage>
    implements CommunicationAdapters {
  RedisTemplate<String, Object> redisTemplate = getRedisTemplate();
  RabbitTemplate rabbitTemplate = getRabbitTemplate();
  RedisUtils redisUtils = new RedisUtils(redisTemplate);
  NettyWebsocketHandler nettyWebsocketHandler = getNettyWebsocketHandler();

  public PrivateChatAdapter(Integer type, String text) {
    super(type, text, ChatMessage.class);
  }

  @Override
  public void notice(ChannelHandlerContext channelHandlerContext) {
    UserMessage fromUser = this.data.getFrom();
    String to = this.data.getTo(), from = fromUser.getUsername();
    Map<String, ChannelHandlerContext> privateMap = nettyWebsocketHandler.getPrivateMap();
    // 如果用户一直不在线，每次访问都需要遍历 getConnectUserMsg。确保准确判断对方用户是否在线
    for (Map.Entry<ChannelHandlerContext, UserMessage> entry :
        nettyWebsocketHandler.getConnectUserMsg().entrySet()) {
      UserMessage toUser = entry.getValue();
      if (toUser != null && toUser.getNickname().equals(to)) {
        // 对方用户在线
        ChannelHandlerContext toChannel = entry.getKey();
        // privateMap 以自己的用户名为 key 存储对方的 channel 通道
        privateMap.put(fromUser.getUsername(), toChannel);
        break;
      }
    }
    if (StringUtils.isNotBlank(to)) {
      // 用户选择了用户进行聊天
      if (StringUtils.isBlank(data.getText())) {
        // 用户进入，获取聊天记录
        rabbitTemplate.convertAndSend(
            RabbitMQConfig.PRIVATE_CHAT_MSG_EXCHANGE,
            RabbitMQConfig.PRIVATE_CHAT_MSG_DAO_ROUTING_KEY,
            this.data);
        // 消除 type = 1 的 unread
        String unreadKey = NoticeCacheKey.UN_READ + this.data.getFrom().getUsername();
        List<Message> messages =
            ListsCollect.objCollect(redisUtils.list0Range(unreadKey), Message.class).orElseEmpty();
        if (!messages.isEmpty()) {
          // 找到对应的 type 值
          for (Message message : messages) {
            ChatMessage chatMessage = JSON.parseObject(message.getContext(), ChatMessage.class);
            if (chatMessage.getTo().equals(this.data.getFrom().getNickname())) {
              // 移除
              redisUtils.listRemove(unreadKey, 0, message);
            }
          }
        }
      } else {
        // 发送聊天信息
        if (privateMap.containsKey(from)) {
          // 对方用户在线,拿到对方的 channel
          ChannelHandlerContext toChannel = privateMap.get(from);
          // 根据对方的 channel 获取 userMessage
          UserMessage userMessage = nettyWebsocketHandler.getConnectUserMsg().get(toChannel);
          if (!privateMap.containsKey(userMessage.getUsername())) {
            // 用户在线，但是只有一方存在于聊天室中时。保留未读消息给对方，即添加至 un_read
            Message message = new Message(MessageKey.PRIVATE_CHAT_FLAG);
            UserMessage fromFlag = this.data.getFrom();
            fromFlag.setFlag(false);
            this.data.setFrom(fromFlag);
            this.data.setOnline(true);
            message.json(this.data).currentDate().buildNoticeTime(new Date());
            redisUtils.listHeaderPushAll(
                NoticeCacheKey.UN_READ + userMessage.getUsername(), message);
            writeAndFlushMessage(toChannel, message);
          } else {
            this.data.setPublishTime(new Date());
            writeAndFlush(toChannel, this.data.buildNoticeTime());
          }
          this.data.setOnline(true);
          this.data.setPublishTime(new Date());
          this.data.buildNoticeTime();
          redisUtils.listFooterPushAll(
              ChatroomCacheKey.ONLINE_PRIVATE_CHAT_KEY
                  + buildChatroom(userMessage.getUsername(), from),
              this.data);
        } else {
          // 用户不在线
          // 如果对方不在线不需要做通知，直接让 service 做操作
          this.data.setOnline(false);
          this.data.setPublishTime(new Date());
          this.data.buildNoticeTime();
          rabbitTemplate.convertAndSend(
              RabbitMQConfig.PRIVATE_CHAT_MSG_EXCHANGE,
              RabbitMQConfig.PRIVATE_CHAT_MSG_DAO_ROUTING_KEY,
              this.data);
        }
      }
    }
  }

  @Override
  public Object dispose(ChannelHandlerContext channelHandlerContext) {
    return null;
  }

  /**
   * 构建私聊聊天室
   *
   * @param to 对话用户的用户名
   * @param from 发送对话的用户名
   * @return 用户名聊天室
   */
  public String buildChatroom(String to, String from) {
    if (StringUtils.isNotBlank(to) && StringUtils.isNotBlank(from)) {
      String[] split = (to + "&" + from).split("&");
      Arrays.sort(split);
      return split[0] + "&" + split[1];
    }
    throw new IllegalArgumentException("参数有误");
  }
}
